#include "Jefe.h"

#include <ColaMensaje.h>
#include <MsgCaja.h>
#include <Serializable.h>
#include <ConfigurationFile.h>
#include <Utils.h>

Jefe::Jefe()
:m_ReceptorPlayerosAbierto(false), m_ExitHandler(NULL)
{
	m_FifosEnvioPlayero.clear();
	
	m_Log = Logger::getInstance();
}

Jefe::~Jefe()
{
	m_ContadorPlayerosLibres.liberar();
	
	m_ReceptorClientes.eliminar();
	
	m_ReceptorPlayeros.cerrar();
}

void Jefe::crearReceptorClientes()
{
	std::string nombArchFifoCliente = ConfigurationFile::getProperty(Properties::COLA_CLIENTE);
	unsigned int maxMsgInQueue = Utils::convertSrtToInt(ConfigurationFile::getProperty(Properties::COLA_CLIENTE_CANT_MSG));
	m_ReceptorClientes.crear(nombArchFifoCliente, CreationMode::NO_DEBE_EXISTIR, maxMsgInQueue);
}

void Jefe::crearReceptorPlayeros()
{
	std::string nombreArchivoFifoPlayeros = ConfigurationFile::getProperty(Properties::COLA_PLAYERO);
	bool protectAccess = false;
	m_ReceptorPlayeros.crear(nombreArchivoFifoPlayeros, CreationMode::NO_DEBE_EXISTIR, protectAccess);
	
	// Creo un contador de playeros libres para saber si hay o no sin bloquearme
	m_ContadorPlayerosLibres.crear(ConfigurationFile::getProperty(Properties::KEY_PLAYEROS_DISP), ProjectId::CONTADOR_PLAYEROS);
	m_ContadorPlayerosLibres.escribirSincronizado(0);
}

void Jefe::recibirCliente()
{
	m_Log->debug("JEFE >> Obteniendo cliente de la cola de clientes");
	TCliente cliente;
	
	// Obtengo a los clientes vip si los hay, sino a los normales
	int bytesLeidos = m_ReceptorClientes.leer(-TipoCliente::getLowestPriorityType(), &cliente);
	if(bytesLeidos > 0)
	{
		std::string pathFifocliente(cliente.fifoPath, cliente.fifoPathSize);
		TipoCliente tipoClient(cliente.mtype);
		m_Log->debug("JEFE >> Se recibio al cliente "+pathFifocliente+" que es del tipo "+tipoClient.toString());
		m_ClientQueue.push(pathFifocliente);
	}
	else
	{
		m_Log->error("JEFE >> Ocurrio un error al leer clientes de la cola");
	}
}

bool Jefe::hayPlayerosLibres()
{
	m_Log->debug("JEFE >> Accediendo a la memoria compartida de Contador de Playeros libres");
	return (m_ContadorPlayerosLibres.leerSincronizado() > 0);
}

void Jefe::atenderCliente()
{
	m_Log->debug("JEFE >> Leyendo memoria compartida de playeros libres");
	// Actualizo el contador de playeros libres
	m_ContadorPlayerosLibres.lock();
	unsigned int cantPlayLibres = m_ContadorPlayerosLibres.leer();
	m_Log->debug("JEFE >> Disminuyendo cantidad de playeros libres");
	cantPlayLibres--;
	m_ContadorPlayerosLibres.escribir(cantPlayLibres);
	m_ContadorPlayerosLibres.unlock();
	
	// Dato a enviar:
	std::string pathFifocliente = m_ClientQueue.front();
	m_ClientQueue.pop();
	
	m_Log->debug("JEFE >> Lectura del FIFO receptor de playeros libres");	
	// Destinatario
	std::string pathFifoPlayero = this->obtenerPlayeroLibre();
	
	// Hago el envio
	m_Log->debug("JEFE >> Buscando fifo de playero "+pathFifoPlayero);
	FifoEscritura& fifoEnvioPlayero = this->obtenerFifoEscritura(m_FifosEnvioPlayero, pathFifoPlayero);
	
	m_Log->debug("JEFE >> Enviando instruccion para atender y el path del fifo del cliente al playero "+pathFifoPlayero);
	fifoEnvioPlayero.lock();
	bool shouldLock = false;
	m_MessageTxRx.enviarAtributoAReceptor(fifoEnvioPlayero,MessageKey::INSTRUCCION, InstructionValue::ATENDER, shouldLock);
	m_MessageTxRx.enviarAtributoAReceptor(fifoEnvioPlayero,MessageKey::PATH_FIFO, pathFifocliente, shouldLock);
	fifoEnvioPlayero.unlock();
}

void Jefe::retirarCliente()
{
	std::string pathFifocliente = m_ClientQueue.front();
	m_ClientQueue.pop();
	this->enviarInstruccionCliente(pathFifocliente, InstructionValue::FINALIZAR);
}

void Jefe::cerrarEstacion()
{
	try
	{
		m_Log->debug("JEFE >> Cerrando FIFO de recepcion de clientes");
		// Impido que se registren nuevos clientes
//		m_ReceptorClientes.cerrarRecepcion();
		
		// Finalizo los clientes
/*		if(m_ReceptorClientesAbierto)
		{
			m_Log->debug("Finalizo procesos clientes");
			this->abrirReceptorClientes();
			while(!m_ClientQueue.empty())
			{
				m_Log->debug("Finalizo al cliente que estaba procesando");
				std::string pathFifocliente = m_ClientQueue.front();
				m_ClientQueue.pop();
				this->enviarInstruccionCliente(pathFifocliente, InstructionValue::FINALIZAR);
			}
			while(m_ReceptorClientes.hasContent())
			{
				m_Log->debug("Busco cliente a finalizar en fifo de recepcion");
				std::string pathFifocliente = m_MessageTxRx.obtenerAtributoDeReceptor(m_ReceptorClientes, MessageKey::PATH_FIFO);
				m_Log->debug("Notifico al cliente "+pathFifocliente+" que debe finalizar");
				this->enviarInstruccionCliente(pathFifocliente, InstructionValue::FINALIZAR);
			}
		}*/
		
		// Finalizo los playeros
		if(m_ReceptorPlayerosAbierto)
		{
			m_Log->debug("JEFE >> Finalizo procesos playeros");
			int cantPlayeros = Utils::convertSrtToInt(ConfigurationFile::getProperty(Properties::CANT_PLAYEROS));
			int j = 0;
			for( ; j < cantPlayeros; j++)
			{
				std::string pathFifoPlayero = m_MessageTxRx.obtenerAtributoDeReceptor(m_ReceptorPlayeros, MessageKey::PATH_FIFO);
				bool protectAccess = true;
				FifoEscritura fifoEnvio(pathFifoPlayero, CreationMode::DEBE_EXISTIR, protectAccess);
				fifoEnvio.abrir();
				m_MessageTxRx.enviarAtributoAReceptor(fifoEnvio,MessageKey::INSTRUCCION,InstructionValue::FINALIZAR);
				fifoEnvio.cerrar();
			}
			TFifoContainerIt itPlayeros = m_FifosEnvioPlayero.begin();
			for( ; itPlayeros != m_FifosEnvioPlayero.end(); itPlayeros++)
			{
				delete itPlayeros->second;
			}
			m_FifosEnvioPlayero.clear();
		}
	}
	catch(const BaseException& e)
	{
		std::cerr << "JEFE >> Ocurrio un error al cerrar la estacion:" << e.what() << std::endl;
	}
	
	try
	{
		// Armo un pedido de cierre de caja
		TPedidoCaja pedidoCierreCaja;
		pedidoCierreCaja.mtype = TipoPedidoCaja::TIPO_CERRAR;
		
		// Creo/obtengo la cola donde estan las solicitudes de caja
		std::string keyCaja = ConfigurationFile::getProperty(Properties::KEY_CAJA);
		ColaMensaje<TPedidoCaja> colaPedidosDeCaja(keyCaja, CreationMode::DEBE_EXISTIR);
		
		// Envio la solicitud
		colaPedidosDeCaja.escribir(pedidoCierreCaja);
	}
	catch(const BaseException& e)
	{
		std::cout << "JEFE >> La caja no estaba iniciada. " << e.what() << std::endl;
	}
}

ExitHandler* Jefe::getExitHandler() const
{
	return m_ExitHandler;
}

void Jefe::setExitHandler(ExitHandler* handler)
{
	m_ExitHandler = handler;
}

void Jefe::abrirReceptorPlayeros()
{
	if(!m_ReceptorPlayerosAbierto)
	{
		m_Log->debug("JEFE >> Abriendo FIFO receptor de playeros");
		m_ReceptorPlayeros.abrir();
		m_ReceptorPlayeros.setExitHandler(m_ExitHandler);
		m_ReceptorPlayerosAbierto = true;
	}
}

void Jefe::enviarInstruccionCliente(const std::string& pathFifocliente, const char* instruccion)
{
	m_Log->debug("JEFE >> Buscando fifo de cliente "+pathFifocliente);
	bool protectAccess = true;
	FifoEscritura fifoEnvioCliente = FifoEscritura(pathFifocliente, CreationMode::DEBE_EXISTIR, protectAccess);
	fifoEnvioCliente.abrir();
	
	m_Log->debug("JEFE >> Enviando instruccion al cliente");
	m_MessageTxRx.enviarAtributoAReceptor(fifoEnvioCliente,MessageKey::INSTRUCCION, instruccion);
}

std::string Jefe::obtenerPlayeroLibre()
{
	this->abrirReceptorPlayeros();
	return m_MessageTxRx.obtenerAtributoDeReceptor(m_ReceptorPlayeros, MessageKey::PATH_FIFO);
}

FifoEscritura& Jefe::obtenerFifoEscritura(TFifoContainer& contenedor, const std::string& fifoPathName)
{
	TFifoContainerIt itFifoEscritura = contenedor.find(fifoPathName);
	if(itFifoEscritura != contenedor.end())
	{
		m_Log->debug("JEFE >> Fifo de escritura "+fifoPathName+" encontrado.");
	}
	else
	{
		contenedor[ fifoPathName ] = new FifoEscritura();
		
		m_Log->debug("JEFE >> Fifo de escritura "+fifoPathName+" no encontrado en pool. Creando.");
		itFifoEscritura = contenedor.find(fifoPathName);
		bool protectAccess = true;
		itFifoEscritura->second->crear(fifoPathName, CreationMode::DEBE_EXISTIR, protectAccess);
		
		m_Log->debug("JEFE >> Abriendo fifo de escritura "+fifoPathName);
		itFifoEscritura->second->abrir();
	}
	return *itFifoEscritura->second;
}
